package cn.net.isir.core;

import cn.net.isir.common.CacheManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cn.net.isir.common.ResultObject;
import cn.net.isir.dao.ShopDao;
import cn.net.isir.entity.Ticket;
import cn.net.isir.service.TicketService;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @author liuxubo
 * @filename ControllerAspect.java
 * @package cn.net.isir.core
 * @email lxb@isir.net.cn
 * @date 2018/11/16
 * @comment
 * @since 1.8
 */
@Component
@Aspect
public class ControllerAspect {

    private static Logger logger = Logger.getLogger(ControllerAspect.class);

    @Resource
    private ShopDao shopDao;
//    @Resource
//    private TicketService ticketService;

    //    @Before("execution(* cn.net.isir.controller.*.*.*(..))")
    public void before(JoinPoint join) {
        //获取方法名
        String mathName = join.getSignature().getName();
        //获取参数列表
        List<Object> args = Arrays.asList(join.getArgs());

        MethodSignature signature = (MethodSignature) join.getSignature();
        Method method = signature.getMethod();
//        method.getAnnotation(RequestMapping.class);
        //判断是否有该注解
        boolean isAnnotationPresent = method.isAnnotationPresent(RequestMapping.class);
        if (isAnnotationPresent) {
            RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
            RequestMethod[] methods = methodRequestMapping.method();
            String[] values = methodRequestMapping.value();
            logger.info(methods.length);
            logger.info(values.length);
            logger.info(methods[0] == RequestMethod.GET);
            logger.info(shopDao.selectCount(null));
//            logger.info();(methodRequestMapping.method());
//            logger.info();(methodRequestMapping.method().length);
//            logger.info();(methodRequestMapping.value());
//            logger.info();(methodRequestMapping.value().length);
//            String[] values = methodRequestMapping.value();
//            logger.info();(values.length);
        }

        //没有的话，就在类上搜索，看是否有此注解
        RequestMapping classRequestMapping = AnnotationUtils.findAnnotation(signature.getMethod().getDeclaringClass(), RequestMapping.class);
        if (classRequestMapping != null) {
            String[] values = classRequestMapping.value();
            logger.info(values.length);
        }

        logger.info("前置通知---->before   方法名是:" + mathName + "\t参数列表是:" + args);
    }

    /**
     * 后置通知
     * 在方法返回后执行，无论是否发生异常
     * 不能访问到返回值
     */
//    @After("execution(* cn.net.isir.controller.*.*.*(..))")
    public void after() {
        logger.info("后置通知---->after....");
    }

    /**
     * 环绕通知
     *
     * @param joinPoint 切入点
     * @return 返回值
     * @throws Throwable 异常
     */
    @Around("execution(* cn.net.isir.controller.*.*.*(..)) || execution(* cn.net.isir.core.BasicController.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("================================around================================");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        //判断是否有RequestMapping注解，没有不作处理
        boolean isAnnotationPresent = method.isAnnotationPresent(RequestMapping.class);
        if (!isAnnotationPresent) {
            return joinPoint.proceed();
        }
//        logger.info(signature.getDeclaringTypeName() + "." + method.getName());
        //从类注解上获取注解内容
        String classUri = null;
        RequestMapping classRequestMapping = AnnotationUtils.findAnnotation(signature.getMethod().getDeclaringClass(), RequestMapping.class);
        if (classRequestMapping != null) {
            String[] values = classRequestMapping.value();
//            logger.info(values.length);
            classUri = values[0];
        }
        //获取方法上的注解
        RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
        RequestMethod[] methods = methodRequestMapping.method();
        String[] values = methodRequestMapping.value();
        //获取所有参数列表
        //Object[] args = joinPoint.getArgs();
        //获取request和response
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        //输出请求地址
//        logger.info("RequestURL:" + request.getRequestURL());
        logger.info("RequestURL:" + request.getRequestURL());
        logger.info("Method:" + request.getMethod());

        //判断是否有RequestMapping注解，如果有，则不做判断
        boolean ignored = method.isAnnotationPresent(IgnoredTicket.class);
        if (ignored) {
            logger.info("ignored.");
            return joinPoint.proceed();
        }

        //先判断methods是否可以取到请求方式
        if (methods.length <= 0) {
            return joinPoint.proceed();
        }

        //如果是get请求，直接回
        if (methods[0] == RequestMethod.GET) {
            return joinPoint.proceed();
        }
        if (methods[0] == RequestMethod.OPTIONS) {
            response.setStatus(204);
            return null;
        }
        if (methods[0] == RequestMethod.POST ||
                methods[0] == RequestMethod.PUT ||
                methods[0] == RequestMethod.DELETE) {
//            Object[] args = joinPoint.getArgs();
//            if (args.length > 0) {
////                logger.info("args count:" + args.length);
//                for (int i = 0; i < args.length; i++) {
////                    logger.info("args [" + i + "] type:" + args[i].getClass().getName());
//                }
//            }

            String ticket = request.getParameter("ticket");
            if (ticket == null) {
                logger.info("can not get ticket");
                response.setStatus(400);
                ResultObject resultObject = new ResultObject(400, "can not get ticket", null);
                response.getOutputStream().print(resultObject.toString());
                return resultObject;
            }
            logger.info("ticket:" + ticket);
//            Ticket t = ticketService.selectByTicket(ticket);
            String objString = CacheManager.getTicket(ticket);
            //如果没有携带ticket
            if (objString == null) {
                logger.info("no this ticket");
                response.setStatus(400);
                ResultObject resultObject = new ResultObject(400, "no this ticket", null);
                response.getOutputStream().print(resultObject.toString());
                return resultObject;
            }
            Ticket t = JSONObject.parseObject(objString, Ticket.class);
            //如果已使用，返回上次的结果
            if ("used".equals(t.getStatus())) {
                logger.info("ticket is already used");
                logger.info(t.getResultJson());
                String currentRequestMethod = signature.getDeclaringTypeName() + "." + method.getName();
                //使用了已使用过的ticket，而且本次请求的方法跟上次不一样
                if (!currentRequestMethod.equals(t.getMethod())) {
                    ResultObject resultObject = new ResultObject(400, "ticket is already. request the error method.", null);
                    response.setStatus(400);
                    response.getOutputStream().print(resultObject.toString());
                    return resultObject;
                }
                Class clazz = signature.getReturnType();
                Object o = JSONObject.parseObject(t.getResultJson(), clazz);
//                logger.info(JSON.toJSONString(o));
                return o;
            }
            //如果未使用，执行相应方法
            if ("unused".equals(t.getStatus())) {
                logger.info("ticket is unused");
                //执行方法
                Object o = joinPoint.proceed();
                //更新ticket
                t.setUsedTime(new Date());
                t.setMethod(signature.getDeclaringTypeName() + "." + method.getName());
                t.setUri(classUri + values[0]);
                //获取返回值
                t.setResultJson(JSON.toJSONString(o));
                //设置为已使用
                t.setStatus("used");
//                ticketService.update(t);
                CacheManager.setTicket(ticket, t);

                logger.info("response:" + JSON.toJSONString(o));
                return o;
            }
        }
        return joinPoint.proceed();
    }
}
